
#include "stdafx.h"
////////////////////////////////////////////////////////////
void ConvertToAddrinfoStdList (
    __in  const ADDRINFOA*                ai,
    __out std::list<ADDRINFOA_STD_LIST>&  ai_list
    )
{
    const ADDRINFOA*    ai_ptr = ai;
    ADDRINFOA_STD_LIST  current;
    
    while (ai_ptr)
    {
        current.ai_flags = ai_ptr->ai_flags;
        current.ai_family = ai_ptr->ai_family;
        current.ai_socktype = ai_ptr->ai_socktype;
        current.ai_protocol = ai_ptr->ai_protocol;
        current.ai_addrlen = ai_ptr->ai_addrlen;
        current.ai_canonname = 
            (ai_ptr->ai_canonname == NULL) ? "" : ai_ptr->ai_canonname;
        
        if (ai_ptr->ai_addr)
        {
            current.ai_addr_val.sa_family = ai_ptr->ai_addr->sa_family;
            strncpy_s (current.ai_addr_val.sa_data, 14, ai_ptr->ai_addr->sa_data, 14);
        } else {
            current.ai_addr_val.sa_family = -1;
        }

        ai_list.push_back(current);
        ai_ptr = ai_ptr->ai_next;
    }
}

////////////////////////////////////////////////////////////
void ConvertToAddrinfoStdList (
    __in  const ADDRINFOW*                ai,
    __out std::list<ADDRINFOW_STD_LIST>&  ai_list
    )
{
    const ADDRINFOW*    ai_ptr = ai;
    ADDRINFOW_STD_LIST  current;
    
    while (ai_ptr)
    {
        current.ai_flags = ai_ptr->ai_flags;
        current.ai_family = ai_ptr->ai_family;
        current.ai_socktype = ai_ptr->ai_socktype;
        current.ai_protocol = ai_ptr->ai_protocol;
        current.ai_addrlen = ai_ptr->ai_addrlen;
        current.ai_canonname = 
            (ai_ptr->ai_canonname == NULL) ? L"" : ai_ptr->ai_canonname;
        
        if (ai_ptr->ai_addr)
        {
            current.ai_addr_val.sa_family = ai_ptr->ai_addr->sa_family;
            strncpy_s (current.ai_addr_val.sa_data, 14, ai_ptr->ai_addr->sa_data, 14);
        } else {
            current.ai_addr_val.sa_family = -1;
        }

        ai_list.push_back(current);
        ai_ptr = ai_ptr->ai_next;
    }
}

////////////////////////////////////////////////////////////
void CreateAddrinfoFromStdList ( 
    __in  std::list<ADDRINFOA_STD_LIST>  ai_list,
    __out ADDRINFOA**                    ai
    )
{
    if (ai_list.size() == 0) {
        *ai = NULL;
        return;
    }

    *ai = new ADDRINFOA;
    ADDRINFOA*          ai_ptr = *ai;
    ADDRINFOA_STD_LIST* current;

    do
    {
        current = &ai_list.front();
        
        ai_ptr->ai_flags = current->ai_flags;
        ai_ptr->ai_family = current->ai_family;
        ai_ptr->ai_socktype = current->ai_socktype;
        ai_ptr->ai_protocol = current->ai_protocol;
        ai_ptr->ai_addrlen = current->ai_addrlen;
        if (current->ai_canonname == "")
        {
            ai_ptr->ai_canonname = NULL;
        } else {
            ai_ptr->ai_canonname = new char[current->ai_canonname.length()+1];
            strcpy_s (ai_ptr->ai_canonname, current->ai_canonname.length()+1,
                current->ai_canonname.c_str());
        }
        short tmp_cmp = current->ai_addr_val.sa_family;
        if (tmp_cmp != -1)
        {
            ai_ptr->ai_addr = new SOCKADDR;
            ai_ptr->ai_addr->sa_family = current->ai_addr_val.sa_family;
            strncpy_s (ai_ptr->ai_addr->sa_data, 14, current->ai_addr_val.sa_data, 14);
        } else {
            ai_ptr->ai_addr = NULL;
        }
        if (ai_list.size() != 1) {
            ai_ptr->ai_next = new ADDRINFOA;
        } else {
            ai_ptr->ai_next = NULL;
        }

        ai_ptr = ai_ptr->ai_next;
        ai_list.pop_front();        
    } while (!ai_list.empty());
}

////////////////////////////////////////////////////////////
void CreateAddrinfoFromStdList ( 
    __in  std::list<ADDRINFOW_STD_LIST>  ai_list,
    __out ADDRINFOW**                    ai
    )
{
    if (ai_list.size() == 0) {
        *ai = NULL;
        return;
    }

    *ai = new ADDRINFOW;
    ADDRINFOW*          ai_ptr = *ai;
    ADDRINFOW_STD_LIST* current;

    do
    {
        current = &ai_list.front();
        
        ai_ptr->ai_flags = current->ai_flags;
        ai_ptr->ai_family = current->ai_family;
        ai_ptr->ai_socktype = current->ai_socktype;
        ai_ptr->ai_protocol = current->ai_protocol;
        ai_ptr->ai_addrlen = current->ai_addrlen;
        if (current->ai_canonname == L"")
        {
            ai_ptr->ai_canonname = NULL;
        } else {
            ai_ptr->ai_canonname = new wchar_t[current->ai_canonname.length()+1];
            wcscpy_s (ai_ptr->ai_canonname, current->ai_canonname.length()+1,
                current->ai_canonname.c_str());
        }
        short tmp_cmp = current->ai_addr_val.sa_family;
        if (tmp_cmp != -1)
        {
            ai_ptr->ai_addr = new SOCKADDR;
            ai_ptr->ai_addr->sa_family = current->ai_addr_val.sa_family;
            strncpy_s (ai_ptr->ai_addr->sa_data, 14, current->ai_addr_val.sa_data, 14);
        } else {
            ai_ptr->ai_addr = NULL;
        }
        if (ai_list.size() != 1) {
            ai_ptr->ai_next = new ADDRINFOW;
        } else {
            ai_ptr->ai_next = NULL;
        }

        ai_ptr = ai_ptr->ai_next;
        ai_list.pop_front();        
    } while (!ai_list.empty());
}

////////////////////////////////////////////////////////////
void DestroyAddrinfoFromStdList ( 
    __in ADDRINFOA*  ai
    )
{
    ADDRINFOA*  ai_ptr = ai;

    if (ai == NULL) {
        return;
    }
    
    if (ai_ptr->ai_canonname != NULL)
    {
        delete[] ai_ptr->ai_canonname;
    }
    if (ai_ptr->ai_addr != NULL)
    {
        delete ai_ptr->ai_addr;
    }
    ADDRINFOA* tmp = ai_ptr;
    ai_ptr = ai_ptr->ai_next;
    delete tmp;
    
    while (ai_ptr)
    {
        if (ai_ptr->ai_canonname != NULL)
        {
            delete[] ai_ptr->ai_canonname;
        }
        if (ai_ptr->ai_addr != NULL)
        {
            delete ai_ptr->ai_addr;
        }

        ADDRINFOA* tmp = ai_ptr;
        ai_ptr = ai_ptr->ai_next;
        delete tmp; 
    }
    ai = NULL;
}

////////////////////////////////////////////////////////////
void DestroyAddrinfoFromStdList ( 
    __in ADDRINFOW*  ai
    )
{
    ADDRINFOW*  ai_ptr = ai;

    if (ai == NULL) {
        return;
    }
    
    if (ai_ptr->ai_canonname != NULL)
    {
        delete[] ai_ptr->ai_canonname;
    }
    if (ai_ptr->ai_addr != NULL)
    {
        delete ai_ptr->ai_addr;
    }
    ADDRINFOW* tmp = ai_ptr;
    ai_ptr = ai_ptr->ai_next;
    delete tmp;
    
    while (ai_ptr)
    {
        if (ai_ptr->ai_canonname != NULL)
        {
            delete[] ai_ptr->ai_canonname;
        }
        if (ai_ptr->ai_addr != NULL)
        {
            delete ai_ptr->ai_addr;
        }

        ADDRINFOW* tmp = ai_ptr;
        ai_ptr = ai_ptr->ai_next;
        delete tmp; 
    }
    ai = NULL;
}

////////////////////////////////////////////////////////////
bool CompareAddrinfo (ADDRINFOA* frst, ADDRINFOA* snd)
{
    PADDRINFOA frst_ptr = frst;
    PADDRINFOA snd_ptr = snd;
    
    while (frst_ptr)
    {
        if (frst_ptr->ai_flags != snd_ptr->ai_flags)
            return false;
        if (frst_ptr->ai_family != snd_ptr->ai_family)
            return false;
        if (frst_ptr->ai_socktype != snd_ptr->ai_socktype)
            return false;
        if (frst_ptr->ai_protocol != snd_ptr->ai_protocol)
            return false;
        if (frst_ptr->ai_addrlen != snd_ptr->ai_addrlen)
            return false;
        if (frst_ptr->ai_canonname && snd_ptr->ai_canonname) {
            if ( strcmp(frst_ptr->ai_canonname, snd_ptr->ai_canonname) )
                return false;
        } else if (frst_ptr->ai_canonname != snd_ptr->ai_canonname)
            return false;
        if (frst_ptr->ai_addr && snd_ptr->ai_addr) {
            if (frst_ptr->ai_addr->sa_family != snd_ptr->ai_addr->sa_family)
                return false;
            if ( strncmp (frst_ptr->ai_addr->sa_data, snd_ptr->ai_addr->sa_data, 14) )
                return false;
        } else if (frst_ptr->ai_addr != snd_ptr->ai_addr)
            return false;
        frst_ptr = frst_ptr->ai_next;
        snd_ptr = snd_ptr->ai_next;
    }
    return true;
}

////////////////////////////////////////////////////////////
bool CompareAddrinfo (ADDRINFOW* frst, ADDRINFOW* snd)
{
    PADDRINFOW frst_ptr = frst;
    PADDRINFOW snd_ptr = snd;
    
    while (frst_ptr)
    {
        if (frst_ptr->ai_flags != snd_ptr->ai_flags)
            return false;
        if (frst_ptr->ai_family != snd_ptr->ai_family)
            return false;
        if (frst_ptr->ai_socktype != snd_ptr->ai_socktype)
            return false;
        if (frst_ptr->ai_protocol != snd_ptr->ai_protocol)
            return false;
        if (frst_ptr->ai_addrlen != snd_ptr->ai_addrlen)
            return false;
        if (frst_ptr->ai_canonname && snd_ptr->ai_canonname) {
            if ( wcscmp(frst_ptr->ai_canonname, snd_ptr->ai_canonname) )
                return false;
        } else if (frst_ptr->ai_canonname != snd_ptr->ai_canonname)
            return false;
        if (frst_ptr->ai_addr && snd_ptr->ai_addr) {
            if (frst_ptr->ai_addr->sa_family != snd_ptr->ai_addr->sa_family)
                return false;
            if ( strncmp (frst_ptr->ai_addr->sa_data, snd_ptr->ai_addr->sa_data, 14) )
                return false;
        } else if (frst_ptr->ai_addr != snd_ptr->ai_addr)
            return false;
        frst_ptr = frst_ptr->ai_next;
        snd_ptr = snd_ptr->ai_next;
    }
    return true;
}

TEST(getaddrinfoTest, getaddrinfo_in_inout)
{
    const char* ip = "127.0.0.1";
    const char* port = "55555";

    ADDRINFOA   aiHints;
    ZeroMemory( &aiHints, sizeof(aiHints) );
    aiHints.ai_family = AF_INET;
    aiHints.ai_socktype = SOCK_STREAM;
    aiHints.ai_protocol = IPPROTO_TCP;
    
    Header header(GetCurrentThreadId(), "getaddrinfo");
    getaddrinfo_in_inout getaddrinfoIN(ip, port, &aiHints);
    getaddrinfo_in_inout getaddrinfoOUT;

    std::vector<char> buffer;
    Serialize(header, getaddrinfoIN, buffer);
        
    Deserialize deserialize(buffer);
    deserialize(getaddrinfoOUT);

    //ADDRINFOA aiHints1;
    ADDRINFOA* aiHints1_ptr = NULL; //&aiHints1;
    CreateAddrinfoFromStdList (getaddrinfoOUT.pHints_val_, &aiHints1_ptr);
    getaddrinfoOUT.pHints_ = aiHints1_ptr;

    EXPECT_EQ (header.TID_, deserialize.get_header().TID_);
    EXPECT_EQ (header.func_name_, deserialize.get_header().func_name_);

    EXPECT_EQ (getaddrinfoIN.pNodeName_, getaddrinfoOUT.pNodeName_);
    EXPECT_EQ (getaddrinfoIN.pServiceName_, getaddrinfoOUT.pServiceName_);
    
    if (getaddrinfoIN.pHints_&& getaddrinfoOUT.pHints_)
    {
        EXPECT_TRUE (CompareAddrinfo (getaddrinfoIN.pHints_, getaddrinfoOUT.pHints_));
    }
    else {
        EXPECT_FALSE(getaddrinfoIN.pHints_);
        EXPECT_FALSE(getaddrinfoOUT.pHints_);
    }
    DestroyAddrinfoFromStdList (getaddrinfoOUT.pHints_);
}

TEST(getaddrinfoTest, getaddrinfo_in_inout_NULL_parameters)
{
    const char* ip = "127.0.0.1";
    const char* port = "55555";

    ADDRINFOA   aiHints;
    ZeroMemory( &aiHints, sizeof(aiHints) );
    aiHints.ai_family = AF_INET;
    aiHints.ai_socktype = SOCK_STREAM;
    aiHints.ai_protocol = IPPROTO_TCP;
    
    Header header(GetCurrentThreadId(), "getaddrinfo");
    getaddrinfo_in_inout getaddrinfoIN(ip, port, NULL);
    getaddrinfo_in_inout getaddrinfoOUT;

    std::vector<char> buffer;
    Serialize(header, getaddrinfoIN, buffer);
        
    Deserialize deserialize(buffer);
    deserialize(getaddrinfoOUT);

    //ADDRINFOA aiHints1;
    ADDRINFOA* aiHints1_ptr = NULL; //&aiHints1;
    CreateAddrinfoFromStdList (getaddrinfoOUT.pHints_val_, &aiHints1_ptr);
    getaddrinfoOUT.pHints_ = aiHints1_ptr;

    EXPECT_EQ (header.TID_, deserialize.get_header().TID_);
    EXPECT_EQ (header.func_name_, deserialize.get_header().func_name_);

    EXPECT_EQ (getaddrinfoIN.pNodeName_, getaddrinfoOUT.pNodeName_);
    EXPECT_EQ (getaddrinfoIN.pServiceName_, getaddrinfoOUT.pServiceName_);
    

    if (getaddrinfoIN.pHints_&& getaddrinfoOUT.pHints_)
    {
        EXPECT_TRUE (CompareAddrinfo (getaddrinfoIN.pHints_, getaddrinfoOUT.pHints_));
    }
    else {
        EXPECT_FALSE(getaddrinfoIN.pHints_);
        EXPECT_FALSE(getaddrinfoOUT.pHints_);
    }
    DestroyAddrinfoFromStdList (getaddrinfoOUT.pHints_);
}

TEST(getaddrinfoTest, getaddrinfo_out_inout)
{
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if(iResult != NO_ERROR) {
        printf("Error at WSAStartup().\n");
    }

    char* ip = "127.0.0.1";
    char* port = "55555";
    struct addrinfo aiHints;
    ADDRINFOA*  ppResult = NULL;
    int retVal;

    memset(&aiHints, 0, sizeof(aiHints));
    aiHints.ai_family = AF_INET;
    aiHints.ai_socktype = SOCK_STREAM;
    aiHints.ai_protocol = IPPROTO_TCP;

    if ((retVal = getaddrinfo(ip, port, &aiHints, &ppResult)) != 0)
    {
        printf("getaddrinfo() failed.\n");
    }
    else {
        printf("getaddrinfo() is OK.\n");
    }

    int ret = 1;
    /*ADDRINFOA   pResult;
    ADDRINFOA*  ppResult = &pResult;
    ZeroMemory( ppResult, sizeof(ADDRINFOA) );
    ppResult->ai_family = AF_INET;
    ppResult->ai_socktype = SOCK_STREAM;
    ppResult->ai_protocol = IPPROTO_TCP;*/
    
    Header header(GetCurrentThreadId(), "getaddrinfo");
    getaddrinfo_out_inout getaddrinfoIN(ret, ppResult, 7);
    getaddrinfo_out_inout getaddrinfoOUT;

    std::vector<char> buffer;
    Serialize(header, getaddrinfoIN, buffer);
        
    Deserialize deserialize(buffer);
    deserialize(getaddrinfoOUT);

    //ADDRINFOA ppResult1;
    ADDRINFOA* ppResult1_ptr = NULL; //&ppResult1;
    CreateAddrinfoFromStdList (getaddrinfoOUT.ppResult_val_, &ppResult1_ptr);
    getaddrinfoOUT.ppResult_ = ppResult1_ptr;

    EXPECT_EQ (header.TID_, deserialize.get_header().TID_);
    EXPECT_EQ (header.func_name_, deserialize.get_header().func_name_);

    EXPECT_EQ (getaddrinfoIN.ret_, getaddrinfoOUT.ret_);
    EXPECT_EQ (getaddrinfoIN.wsa_last_error_, getaddrinfoOUT.wsa_last_error_);
    
    if (getaddrinfoIN.ppResult_&& getaddrinfoOUT.ppResult_)
    {
        EXPECT_TRUE (
            CompareAddrinfo (getaddrinfoIN.ppResult_, getaddrinfoOUT.ppResult_));
    }
    else {
        EXPECT_FALSE(getaddrinfoIN.ppResult_);
        EXPECT_FALSE(getaddrinfoOUT.ppResult_);
    }
    freeaddrinfo(ppResult);
    WSACleanup();
    DestroyAddrinfoFromStdList (getaddrinfoOUT.ppResult_);
}

TEST(getaddrinfoTest, getaddrinfo_out_inout_NULL_parameters)
{
    int ret = 1;
    ADDRINFOA   pResult;
    ADDRINFOA*  ppResult = &pResult;
    ZeroMemory( ppResult, sizeof(ADDRINFOA) );
    ppResult->ai_family = AF_INET;
    ppResult->ai_socktype = SOCK_STREAM;
    ppResult->ai_protocol = IPPROTO_TCP;
    
    Header header(GetCurrentThreadId(), "getaddrinfo");
    getaddrinfo_out_inout getaddrinfoIN(ret, NULL, 7);
    getaddrinfo_out_inout getaddrinfoOUT;

    std::vector<char> buffer;
    Serialize(header, getaddrinfoIN, buffer);
        
    Deserialize deserialize(buffer);
    deserialize(getaddrinfoOUT);

    //ADDRINFOA ppResult1;
    ADDRINFOA* ppResult1_ptr = NULL; //&ppResult1;
    CreateAddrinfoFromStdList (getaddrinfoOUT.ppResult_val_, &ppResult1_ptr);
    getaddrinfoOUT.ppResult_ = ppResult1_ptr;

    EXPECT_EQ (header.TID_, deserialize.get_header().TID_);
    EXPECT_EQ (header.func_name_, deserialize.get_header().func_name_);

    EXPECT_EQ (getaddrinfoIN.ret_, getaddrinfoOUT.ret_);
    EXPECT_EQ (getaddrinfoIN.wsa_last_error_, getaddrinfoOUT.wsa_last_error_);
    
    if (getaddrinfoIN.ppResult_&& getaddrinfoOUT.ppResult_)
    {
        EXPECT_TRUE (
            CompareAddrinfo (getaddrinfoIN.ppResult_, getaddrinfoOUT.ppResult_));
    }
    else {
        EXPECT_FALSE(getaddrinfoIN.ppResult_);
        EXPECT_FALSE(getaddrinfoOUT.ppResult_);
    }
    //freeaddrinfo(ppResult);
    DestroyAddrinfoFromStdList (getaddrinfoOUT.ppResult_);
}

TEST(GetAddrInfoWTest, GetAddrInfoW_in_inout)
{
    const wchar_t* ip = L"127.0.0.1";
    const wchar_t* port = L"55555";

    ADDRINFOW   aiHints;
    ZeroMemory( &aiHints, sizeof(aiHints) );
    aiHints.ai_family = AF_INET;
    aiHints.ai_socktype = SOCK_STREAM;
    aiHints.ai_protocol = IPPROTO_TCP;
    
    Header header(GetCurrentThreadId(), "GetAddrInfoW");
    GetAddrInfoW_in_inout GetAddrInfoWIN(ip, port, &aiHints);
    GetAddrInfoW_in_inout GetAddrInfoWOUT;

    std::vector<char> buffer;
    Serialize(header, GetAddrInfoWIN, buffer);
        
    Deserialize deserialize(buffer);
    deserialize(GetAddrInfoWOUT);

    //ADDRINFOA aiHints1;
    ADDRINFOW* aiHints1_ptr = NULL; //&aiHints1;
    CreateAddrinfoFromStdList (GetAddrInfoWOUT.pHints_val_, &aiHints1_ptr);
    GetAddrInfoWOUT.pHints_ = aiHints1_ptr;

    EXPECT_EQ (header.TID_, deserialize.get_header().TID_);
    EXPECT_EQ (header.func_name_, deserialize.get_header().func_name_);

    EXPECT_EQ (GetAddrInfoWIN.pNodeName_, GetAddrInfoWOUT.pNodeName_);
    EXPECT_EQ (GetAddrInfoWIN.pServiceName_, GetAddrInfoWOUT.pServiceName_);
    
    if (GetAddrInfoWIN.pHints_&& GetAddrInfoWOUT.pHints_)
    {
        EXPECT_TRUE (CompareAddrinfo (GetAddrInfoWIN.pHints_, GetAddrInfoWOUT.pHints_));
    }
    else {
        EXPECT_FALSE(GetAddrInfoWIN.pHints_);
        EXPECT_FALSE(GetAddrInfoWOUT.pHints_);
    }
    DestroyAddrinfoFromStdList (GetAddrInfoWOUT.pHints_);
}
